<script lang="ts" setup>
import { message, Upload, type UploadChangeParam, type UploadProps } from 'ant-design-vue'
import { computed, ref, watch } from 'vue'
import { createOssClient, useGenerateFileName } from '@/utils/utils'
import { useListToString } from '@/utils/urlUtils'


// 引入类型
interface propsListType {
  name: string;
  url: string;
}

// 引用声明
const props = defineProps({
  // 图片地址
  modelValue: {
    type: [String, Array],
    default: () => ''
  },

  // 图片数量限制
  limit: {
    type: Number,
    default: () => 1
  },

  // 类型 picture,picture-card,text
  listType: {
    type: String,
    default: () => 'picture-card'
  },

  // tips
  tipsText: {
    type: String,
    default: () => '上传图片'
  },

  // 大小限制(MB)
  fileSize: {
    type: Number,
    default: 10
  },

  // 文件类型, 例如['png', 'jpg', 'jpeg']
  fileType: {
    type: Array,
    default: () => ['jpg', 'jpeg', 'png']
  }
})

// 引用事件
const emits = defineEmits(['update:modelValue', 'input'])

// 响应式数据
const fileList = ref([])
const uploadList = ref([])
const open = ref(false)
const url = ref('')
const number = ref(0)

// 监听事件
watch(() => props.modelValue, (newVal) => {
  console.log(newVal)
  if (newVal) {
    // 首先将值转为数组
    const list = Array.isArray(newVal) ? newVal : props.modelValue.split(',')
    // 然后将数组转为对象数组
    fileList.value = list.map((item: string) => {
      if (item) {
        return { name: item, url: item }
      }
    })
  } else {
    fileList.value = []
  }
}, {
  immediate: true
})

// 自定义上传事件
const handleCustomRequest = async (event: UploadProps['customRequest']) => {
  const { action, data, file, filename, headers, method, onError, onProgress, onSuccess, withCredentials } = event

  // 自定义名字
  const fileName = useGenerateFileName(file.name)

  // 获取ali-oss
  createOssClient().then(async (client: any) => {
    console.log(client)
    onProgress({ ...file, percent: 40 })
    const { res: { status, statusCode, requestUrls: [url] } }: any = await client.put(fileName, file)
    if (status === 200 && statusCode === 200) {
      onProgress({ percent: 100 })
      onSuccess({ url })
    }
  })
}

// 上传文件之前的钩子，参数为上传的文件， 若返回false或者返回 Promise 且被 reject，则停止上传。
const handleBeforeUpload = async (file: UploadProps['beforeUpload']) => {
  // 判断文件大小
  const isLtM = file.size / 1024 / 1024 < props.fileSize

  if (!isLtM) {
    message.error(`文件大小不得大于${props.fileSize}MB!`)
  }

  number.value++
  return isLtM || Upload.LIST_IGNORE
}


// 上传文件改变时的状态，详见 change
const handleChange = (info: UploadChangeParam) => {
  let { file } = info
  if (file.status === 'done') {
    const { url } = file.response
    uploadList.value.push({ name: url, url })
    uploadedSuccessfully()
  } else if (file.status === 'removed') {
    let url = ''
    if (file.response) {
      url = file.response.url
    } else if (file.name && file.url) {
      url = file.url
    }
    fileList.value = fileList.value.filter((item: Array<propsListType>) => item.name !== url)
    emits('update:modelValue', useListToString(fileList.value))
  }
}

// 上传结束处理
const uploadedSuccessfully = () => {
  if (number.value > 0 && uploadList.value.length === number.value) {
    fileList.value = fileList.value
      .filter((f: any) => f.url !== undefined)
      .concat(uploadList.value)
    number.value = 0
    uploadList.value = []
    emits('update:modelValue', useListToString(fileList.value))
  }
}

// 预览图片
const handlePreView = (file: UploadProps) => {
  open.value = true
  url.value = file.url
}


// 接受上传的文件类型
const fileToType = computed(() => {
  return props.fileType.map((extension) => `.${extension}`).join(',')
})
</script>

<template>
  <div class="upload-container">
    <a-upload
      v-model:fileList="fileList"
      :accept="fileToType"
      :beforeUpload="handleBeforeUpload"
      :customRequest="handleCustomRequest"
      :list-type="listType"
      :max-count="limit"
      @change="handleChange"
      @preview="handlePreView"
    >
      <div>
        {{ tipsText }}
      </div>
    </a-upload>
    <a-modal v-model:open="open" :footer="null" title="预览图片">
      <a-image :src="url" width="200" />
    </a-modal>
  </div>
</template>

<style lang="less" scoped>
.upload-container {
  display: block;
}
</style>
